<forEach>
跟 Listbox, Grid 同樣支援 model-driven rendering,也就是基於 ListModel
來繪製畫面,你只要變更 ListModel
物件內容,ZK 會自動幫你更新到瀏覽器頁面。例如下面是一個新增、刪除、排序名字列表的應用:
<groupbox width="450px" apply="quickstart.shadow.ForEachComposer" mold="3d">
<div>
<forEach id="namesList" var="name">
<span sclass="nameTag">
<label value="${name}"/>
</span>
</forEach>
</div>
</groupbox>
<forEach>
id 好讓我可以在控制器中取得參照。var
屬性是把預設的隱含變數名稱 each
改名為 name
,這樣在 <forEach>
內部就必須要用 ${name}
來存取集合物件內每個物件,這是一個增加可讀性的功能,不改也不影響功能。public class ForEachComposer extends SelectorComposer<Component> {
@Wire("::shadow#namesList")
private ForEach namesList;
private ListModelList<String> namesModel;
@Override
public void doAfterCompose(Component comp) throws Exception {
super.doAfterCompose(comp);
namesModel = new ListModelList<String>(new String[] {"Chris", "Elisabeth", "Aaron", "Berta", "Daniel"});
namesList.setItems(namesModel);
namesList.recreate();
setItems()
把初始化好的資料模型 ListModelList
賦予給 nameList
,再呼叫 recreate()
重建 <forEach>
的元件
增加一個 <textbox>
並把事件轉發到根元件上。
<groupbox width="450px" apply="quickstart.shadow.ForEachComposer" mold="3d">
<div>
<forEach id="namesList" var="name">
<span sclass="nameTag">
<label value="${name}"/>
</span>
</forEach>
</div>
<textbox forward="onOK=onAddName" placeholder="New Name + ENTER"/>
</groupbox>
onOK
事件發生在使用者在 <textbox>
內按下 Enter 鍵,這樣好處是可以免去點擊「增加」按鈕的操作<groupbox>
@Override
public void doAfterCompose(Component comp) throws Exception {
super.doAfterCompose(comp);
...
comp.addEventListener("onAddName", (EventListener<ForwardEvent>)this::addName);
}
private void addName(ForwardEvent event) {
Textbox nameInput = (Textbox) event.getOrigin().getTarget();
Optional.ofNullable(nameInput.getValue())
.filter(v -> !namesModel.contains(v)) //ignore duplicates
.ifPresent(namesModel::add);
nameInput.setValue("");
}
addEventListener()
namesModel::add
,zk 就會幫你通知 <forEach>
並更新頁面
在名字後面增加一個 X 圖示來作為「移除」按鈕
<forEach id="namesList" var="name">
<span sclass="nameTag" >
<label value="${name}"/>
<a forward="onClick=onRemoveName(${name})" iconSclass="z-icon-times"/>
</span>
</forEach>
<groupbox>
,這樣的好處是,我可以統一把傾聽器都註冊在 <groupbox>
,比較好維護。如果註冊在內部子元件 <a>
上,如果未來要換成別的元件,就要修改程式onRemoveName(${name})
:這寫法就能把名字當作參數傳進傾聽器中public void doAfterCompose(Component comp) throws Exception {
super.doAfterCompose(comp);
...
comp.addEventListener("onRemoveName", event -> namesModel.remove((String) event.getData()));
}
event.getData()
取得先前傳入的參數(名字)
加2個排序按鈕,分別是升序與降序排序:
<button forward="onSortAsc" iconSclass="z-icon-sort-alpha-asc"/>
<button forward="onSortDesc" iconSclass="z-icon-sort-alpha-desc"/>
呼叫 ListModelList.sort()
來排序即可
@Override
public void doAfterCompose(Component comp) throws Exception {
super.doAfterCompose(comp);
...
comp.addEventListener("onSortAsc", event -> namesModel.sort(String.CASE_INSENSITIVE_ORDER));
comp.addEventListener("onSortDesc", event -> namesModel.sort(String.CASE_INSENSITIVE_ORDER.reversed()));
...
}
增加一個清除全部按鈕
<button forward="onClearAll" iconSclass="z-icon-user-times" tooltiptext="clear all"/>
呼叫 ListModelList.clear()
@Override
public void doAfterCompose(Component comp) throws Exception {
super.doAfterCompose(comp);
...
comp.addEventListener("onClearAll", event -> namesModel.clear());
...
}
透過以上例子應該可以看出,用 <forEach>
再搭配 ListModelList
即可以快速做出新增、刪除等各種操作集合物件的功能。